package cn.facetoken.homer;

import android.content.Context;
import android.net.Uri;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

import com.nnf.nnfkitlib.NNF_FaceInfo;

import java.util.Arrays;

import cn.openbiotoken.BiometricsFactory;
import cn.openbiotoken.BiometricsFeature;
import cn.openbiotoken.BiometricsFeatureExtractor;
import cn.openbiotoken.android.facetoken.HomoSapiensEyeprintUriBiometricsFactory;
import cn.openbiotoken.android.facetoken.HomoSapiensFaceUriBiometricsFactory;

@Entity(tableName = "face_sample")
public class FaceSample {

    @PrimaryKey(autoGenerate = true)
    public long id;

    @ColumnInfo(name = "name")
    public String name;

    @ColumnInfo(name = "sample_uri")
    //@NonNull
    public String sampleUri;

    @ColumnInfo(name = "face_feature")
    public String faceFeature;

    @ColumnInfo(name = "eyeprint_feature")
    public String eyeprintFeature;

    @ColumnInfo(name = "created_at")
    public long createdAt;

    @ColumnInfo(name = "updated_at")
    public long updatedAt;

    public transient float similarity;

    public void initialize() {
        updatedAt = createdAt = System.currentTimeMillis();
    }

    public void setFaceFeature(@NonNull final float[] array) {
        Log.d(getClass().getSimpleName(), String.format("setFaceFeature(), array.length = %d", array.length));
        this.faceFeature = floatsToString(array);
        Log.d(getClass().getSimpleName(), String.format("setFaceFeature(), after set, faceFeature = %s", this.faceFeature));
    }

    @NonNull
    public float[] getFaceFeature() {
        return stringToFloats(this.faceFeature);
    }

    @NonNull
    public String getFaceFeatureText() {
        return faceFeature;
    }

    public void setEyeprintFeature(@NonNull final float[] array) {
        Log.d(getClass().getSimpleName(), String.format("setEyeprintFeature(), array.length = %d", array.length));
        this.eyeprintFeature = floatsToString(array);
        Log.d(getClass().getSimpleName(), String.format("setEyeprintFeature(), after set, eyeprintFeature = %s", this.eyeprintFeature));
    }

    @NonNull
    public float[] getEyeprintFeature() {
        return stringToFloats(this.eyeprintFeature);
    }

    @NonNull
    public String getEyeprintFeatureText() {
        return eyeprintFeature;
    }

    private String floatsToString(final float[] array) {
        return null==array ? "" : Arrays.toString(array);
    }

    @NonNull
    private float[] stringToFloats(@NonNull final String text) {
        final String[] lines = text.substring(1, text.length()-2).split(",\\s?");
        final float[] array = new float[lines.length];
        int index = 0;
        for (String line: lines) {
            array[index++] = Float.parseFloat(line.trim());
        }
        return array;
    }

    @NonNull
    public PhotoRegistration<String> toPhotoRegistration() {
        PhotoRegistration<String> photoRegistration = new PhotoRegistration<>(id, sampleUri, name, getFaceFeatureText(), getEyeprintFeatureText());
        photoRegistration.similarity = similarity;
        return photoRegistration;
    }

    public void extractEyeprintFeature(@NonNull final Context context) {
        setEyeprintFeature(extractBiometricsFeature(new HomoSapiensEyeprintUriBiometricsFactory(context)));
    }

    public void extractFaceFeature(@NonNull final Context context) {
        setFaceFeature(extractBiometricsFeature(new HomoSapiensFaceUriBiometricsFactory(context)));
    }

    @NonNull
    private float[] extractBiometricsFeature(@NonNull final BiometricsFactory<NNF_FaceInfo, float[], Uri> factory) {
        @NonNull final BiometricsFeatureExtractor<float[]> extractor = factory.createFeatureExtractor(Uri.parse(sampleUri));
        @NonNull final BiometricsFeature<float[]> feature = extractor.extract();
        return feature.getFeature();
    }

    public float getSimilarity() {
        return similarity;
    }

    public void setSimilarity(final float similarity) {
        this.similarity = similarity;
    }
}
